import {PreLoader} from "./loader/PreLoader";
import {assert} from "../utils/assert";
import {ModuleManager} from "./managers/ModuleManager";
import {Bundle} from "./core/Bundle";
import jQuery from 'jquery';
import {NativeEventBridge} from "./eventbus/NativeEventBridge";
import {requestParams} from "../utils/request";
import {supportFunction} from "../utils/supportFunction";
import {getPlayerConfig} from "../utils/playerUtils";
import {urlUtils} from "../utils/urlUtils";

let running = false;

export const MidwayConfig = {
    BasePath: '',
    RunEnvironment: 'product',
    ConfigPath: 'configuration',
    JsLibraryPath: 'js-library',
    BundlePath: 'bundles',
    Terminal: 'none',
    Runtime: 'web',
    NativeInterface: undefined,
    NativeBridgeClass: undefined
};

export const Midway = {};

Object.defineProperty(Midway, 'version', {
    value: "1.2.7"
});

Object.defineProperty(Midway, 'config', {
    value: function (options) {
        if (supportFunction.hasProp(Midway, 'hasReady')) {
            console.error("The container has been initialized and can not be modified");
            return;
        }

        if (options.BundlePath !== undefined && typeof options.BundlePath === 'string') {
            MidwayConfig.BundlePath = options.BundlePath;
        }
        if (options.BasePath !== undefined && typeof options.BasePath === 'string') {
            MidwayConfig.BasePath = options.BasePath;
        }
        if (options.ConfigPath !== undefined && typeof options.ConfigPath === 'string') {
            MidwayConfig.ConfigPath = options.ConfigPath;
        }
        if (options.RunEnvironment !== undefined && typeof options.RunEnvironment === 'string') {
            if(options.RunEnvironment === 'product' || options.RunEnvironment === 'development') {
                MidwayConfig.RunEnvironment = options.RunEnvironment;
            }
        }
        if(options.Extra !== undefined && typeof options.Extra === "object") {
            MidwayConfig.Extra = options.Extra;
        }
        //如果BasePath包含HTTP或HTTPS则认为是在线场景
        Object.defineProperty(MidwayConfig, 'Online', {value: urlUtils.isHttpAddress(MidwayConfig.BasePath)});
        //配置运行时
        const runtimeMapping = options.RuntimeMapping || {'AndroidInterface': 'mobile', 'iOSInterface': 'mobile', 'PCInterface': 'pc'};
        for (let key in runtimeMapping) {
            if (key in window) {
                MidwayConfig.Runtime = runtimeMapping[key] || 'web';
                MidwayConfig.NativeInterface = key;
                MidwayConfig.NativeBridgeClass = options.NativeBridgeClass || "IcrJsBridge";
                break;
            }
        }
        //配置教师与学生端
        let terminal = 'none';
        if (requestParams.params['terminal']) {
            terminal = requestParams.params['terminal'].toLowerCase();
        }
        MidwayConfig.Terminal = terminal;
    }
});

Object.defineProperty(Midway, 'runMidway', {
    value: function (options) {
        if (running) {
            return;
        }
        //开始执行预加载过程
        running = true;
        const file = (options && options.file) ? options.file : "config.json";
        const code = (options && options.code) ? options.code : undefined;
        const version = (options && options.version) ? options.version : undefined;
        //配置原生桥接层
        if (MidwayConfig.Runtime !== 'web' && MidwayConfig.NativeInterface !== undefined) {
            const bridge = new NativeEventBridge(window[MidwayConfig.NativeInterface]);
            window.Bridge = bridge;
            if (requestParams.params['iframeBridge'] === 'true' && typeof window.top) {
                window.top.Bridge = bridge;
            }
        }

        return getPlayerConfig(MidwayConfig, code, version, file).then(config => {
            //构建Bundle配置
            const preLoader = new PreLoader();
            //预加载完成后
            return preLoader.load(config)
                .done(() => {
                    //在Midway中设置一个已经准备完毕的标识
                    Object.defineProperty(Midway, 'hasReady', {value: true});
                    Object.defineProperty(Midway, 'Settings', {value: MidwayConfig});
                    Object.freeze(MidwayConfig);
                    console.log("Preload Over");
                });
        }).fail(()=>{
            console.error("Unable to read the online player's configuration file");
        });
    }
});

Object.defineProperty(Midway, 'MidwayModule', {value: MidwayModule});

Object.defineProperty(Midway, 'FixPropertyConfig', {value: FixPropertyConfig});

Object.defineProperty(Midway, 'Bundle', {value: Bundle});

function MidwayModule(options) {
    const {
        code = options.code,
        template = options.template || undefined,
        style = options.style || undefined,
        dependLibs = options.dependLibs || []
    } = options;
    //Bundle的名称不能为空
    assert(code === undefined, 'the module name is empty!');
    let styleValid = (style !== undefined), templateValid = (template !== undefined);
    let element;
    if (templateValid === true) {
        //检查模板
        element = jQuery.parseHTML(template);
        if (element.length > 1) {
            console.warn('component template can only one root element, but got ' + element.length);
            templateValid = false;
        }
    }
    if (styleValid && typeof style.toString !== 'function') {
        console.warn('component style not valid, please check it!');
        styleValid = false;
    }

    return module => {
        Object.defineProperty(module.prototype, '__dependLibs', {value: dependLibs});
        Object.defineProperty(module.prototype, '__code', {value: code});
        //允许组件没有模板
        if (templateValid) {
            Object.defineProperty(module.prototype, '__template', {value: element[0]});
        }
        //允许组件没有样式
        if (styleValid) {
            Object.defineProperty(module.prototype, '__style', {value: style});
        }
        //将target添加到管理对象中
        ModuleManager.getInstance().add(code, module);
    };
}

function FixPropertyConfig(options) {
    const {
        configurable = options.configurable || false,
        writable = options.writable || false,
        enumerable = options.enumerable || false
    } = options;

    return function (target, key, descriptor) {
        descriptor.configurable = configurable;
        descriptor.writable = writable;
        descriptor.enumerable = enumerable;
        return descriptor;
    }
}







